//=============================================================================

#include <windows.h>
#include <gl/gl.h>
#include <fstream>
#include "../inc/aSurface.h"
#include "../inc/aAux.h"

//=============================================================================

using namespace std;

//=============================================================================

aSurface::aSurface() : bbOffset(0.01f) 
{
  vertexes = 0;
  uv = 0;
  normals = 0;
  vertexesCount = 0;
  trianglesCount = 0;
  draw = 1;
  drawTriangles = 1;
  drawPoints = 0;
  drawLines = 1;
  drawNormals = 0;
  drawBB = 0;
  drawOrigin = 0;
  color[0] = 255;
  color[1] = 255;
  color[2] = 255;
}

//=============================================================================

void aSurface::asDrawSurface() const
{
  if (!draw)
    return;

  bool exist = skin.asIsExist();

  glPushMatrix();
  glTranslatef(origin.x, origin.y, origin.z);
	glTexCoordPointer(2, GL_FLOAT, 0, uv);
  glVertexPointer(3, GL_FLOAT, 0, vertexes);
  glNormalPointer(GL_FLOAT, 0, normals);

  if (exist)
    skin.asBindSkin();
  else
    glDisable(GL_TEXTURE_2D);

  if (drawPoints)
  {
    if (exist)
      glDisable(GL_TEXTURE_2D);

    glColor3ub(0, 255, 0);
	  glDrawArrays(GL_POINTS, 0, vertexesCount);

    if (exist)
      glEnable(GL_TEXTURE_2D);
  }
  if (drawLines)
  {
    if (exist || !drawPoints)
      glDisable(GL_TEXTURE_2D);

    glColor3ub(0, 0, 255);
    glDrawArrays(GL_LINES, 0, vertexesCount);

    if (exist || !drawPoints)
      glEnable(GL_TEXTURE_2D);
  }
  if (drawTriangles)
  {
    glColor3ub(color[0], color[1], color[2]);
	  glDrawArrays(GL_TRIANGLES, 0, vertexesCount);
  }
  if (drawBB && bb)
  {
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);
	  glLineWidth(3);

    glVertexPointer(3, GL_FLOAT, 0, bb);
    glColor4ub(0, 255, 0, 90);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glColor4ub(255, 0, 0, 255);
    glDrawArrays(GL_LINE_STRIP, 0, 36);
    glColor4ub(0, 0, 255, 255);
    glDrawArrays(GL_POINTS, 0, 36);
    
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
	  glLineWidth(1);
  }
  if (drawOrigin)
  {
    glDisable(GL_TEXTURE_2D);
    glColor3ub(255, 0, 0);
    glBegin(GL_LINES);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(1.0f, 0.0f, 0.0f);
    glEnd();
    glColor3ub(0, 255, 0);
    glBegin(GL_LINES);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(0.0f, 1.0f, 0.0f);
    glEnd();
    glColor3ub(0, 0, 255);
    glBegin(GL_LINES);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(0.0f, 0.0f, 1.0f);
    glEnd();
    glEnable(GL_TEXTURE_2D);
  }
  if (drawNormals)
    asDrawNormals();
  glPopMatrix();
}

//=============================================================================

bool aSurface::asIsNULL() const
{
  if (!vertexes || (trianglesCount <= 0))
    return 1;

  return 0;
}

//=============================================================================
void aSurface::asTranslateA(const aVector& vec)
{
  unsigned long num;

  for (unsigned long i = 0; i < vertexesCount; ++i)
  {
    num = i*3;
    vertexes[num] += vec.x;
    vertexes[num + 1] += vec.y;
    vertexes[num + 2] += vec.z;
  }
}

//=============================================================================

void aSurface::asScaleA(const aVector& vec)
{
  unsigned long num;

  for (unsigned long i = 0; i < vertexesCount; ++i)
  {
    num = i*3;
    vertexes[num] *= vec.x;
    vertexes[num + 1] *= vec.y;
    vertexes[num + 2] *= vec.z;
  }
}

//=============================================================================

aseRet aSurface::asLoadSkin(const aString& path)
{
  if (path.asGetLength() <= 0)
    return SURF_NULLED_FILE_PATH;

  skin.asAddTexture(path);

  return SURF_DONE;
}

//=============================================================================

void aSurface::asSpawnMemory(unsigned int tCount)
{
  if (tCount <= 0)
    return;

  trianglesCount = tCount;
  vertexesCount = trianglesCount*3;

  vertexes = new float[vertexesCount*3];
  uv = new float[vertexesCount*2];
  normals = new float[vertexesCount*3];
  
  memset(vertexes, 0, sizeof(float)*vertexesCount*3);
  memset(uv, 0, sizeof(float)*vertexesCount*2);
  memset(normals, 0, sizeof(float)*vertexesCount*3);
}

//=============================================================================

void aSurface::asGenerateNormals()
{
  if (!normals || asIsNULL())
    return;

  unsigned long i, num;
  aVector vec, vec2, vec3;

  for (i = 0; i < trianglesCount; ++i)
  {
    num = i*9;
    vec.avSet(vertexes[num], vertexes[num + 1], vertexes[num + 2]);
    vec2.avSet(vertexes[num + 3], vertexes[num + 4], vertexes[num + 5]);
    vec3.avSet(vertexes[num + 6], vertexes[num + 7], vertexes[num + 8]);
    vec = aAux::aaGetInstance().aaNormalize(aAux::aaGetInstance().aaGetNormal(vec, vec2, vec3));
    normals[num] = vec.x;
    normals[num + 1] = vec.y;
    normals[num + 2] = vec.z;
  }
}

//=============================================================================

void aSurface::asRemoveSurfaceData()
{
  if (asIsNULL())
    return;

  if (vertexesCount)
  {
    if (vertexes)
    {
      delete [] vertexes;
      
      vertexes = 0;
    }
    if (uv)
    {
      delete [] uv;

      uv = 0;
    }
    if (normals)
    {
      delete [] normals;

      normals = 0;
    }
  }
  bbMax.avSet(0.0f, 0.0f, 0.0f);
  bbMin.avSet(0.0f, 0.0f, 0.0f);
  bbDiff.avSet(0.0f, 0.0f, 0.0f);
  vertexesCount = 0;
  trianglesCount = 0;
}

//=============================================================================

aseRet aSurface::asCollectSurfaceData(void* vuvn, size_t wholeSize) const
{
  if (wholeSize <= 0)
    return SURF_COL_BAD_DATA_VOLUME;

  if (!vuvn)
    return SURF_COL_BAD_DATA_POINTER;

  if (asIsNULL())
    return SURF_COL_NO_VERTEXES_DATA;

  if (!uv)
    return SURF_COL_NO_UV_DATA;

  if (!normals)
    return SURF_COL_NO_NORMALS_DATA;

  float* tmpPointer = (float*)vuvn;
  size_t mult = sizeof(float)*vertexesCount;

  memcpy(tmpPointer, vertexes, mult*3);//get vertexes data
  memcpy(&tmpPointer[vertexesCount*3], uv, mult*2);//get uv data
  memcpy(&tmpPointer[vertexesCount*5], normals, mult*3);//get normals data

  return SURF_DONE;
}

//=============================================================================

aseRet aSurface::asAbsorbSurfaceData(char* data, int vCount)
{
  if (!data)
    return SURF_ABS_BAD_DATA_POINTER;

  if (vCount <= 0)
    return SURF_ABS_BAD_VERTEXES_COUNT;

  size_t mult = sizeof(float)*vCount;

  asSpawnMemory(vCount/3);

  float* tmpPointer = (float*)data;

  memcpy(vertexes, tmpPointer, mult*3);
  memcpy(uv, &tmpPointer[vertexesCount*3], mult*2);
  memcpy(normals, &tmpPointer[vertexesCount*5], mult*3);

  asGetBB();

  return SURF_DONE;
}

//=============================================================================

void aSurface::asDrawNormals() const
{
  unsigned long num;

  glDisable(GL_TEXTURE_2D);
  glColor3ub(255, 0, 0);
  for (unsigned long i = 0; i < vertexesCount; ++i)
  {
    num = i*3;
    glBegin(GL_LINES);
      glVertex3f(vertexes[num], vertexes[num + 1], vertexes[num + 2]);
      glVertex3f(vertexes[num] + normals[num], vertexes[num + 1] + normals[num + 1], vertexes[num + 2] + normals[num + 2]);
    glEnd();
  }
  glEnable(GL_TEXTURE_2D);
}

//=============================================================================

void aSurface::asGetBB()
{
  if (asIsNULL())
    return;

  unsigned long vCount = 0;
  unsigned long num;
  aVector v[8];

  while(vCount != vertexesCount)
  {
    num = vCount*3;
    if (bbMin.x > vertexes[num])
      bbMin.x = vertexes[num];
    if (bbMin.y > vertexes[num + 1])
      bbMin.y = vertexes[num + 1];
    if (bbMin.z > vertexes[num + 2])
      bbMin.z = vertexes[num + 2];

    if (bbMax.x < vertexes[num])
      bbMax.x = vertexes[num];
    if (bbMax.y < vertexes[num + 1])
      bbMax.y = vertexes[num + 1];
    if (bbMax.z < vertexes[num + 2])
      bbMax.z = vertexes[num + 2];

    ++vCount;
  }

  bbMin.x -= bbOffset;
  bbMin.y -= bbOffset;
  bbMin.z -= bbOffset;
  bbMax.x += bbOffset;
  bbMax.y += bbOffset;
  bbMax.z += bbOffset;

  bbDiff = aAux::aaGetInstance().aaSubtrack(bbMax, bbMin);

  v[0] = aVector(bbMin.x, bbMin.y, bbMax.z);
  v[1] = aVector(bbMin.x, bbMax.y, bbMax.z);
  v[2] = aVector(bbMax.x, bbMax.y, bbMax.z);
  v[3] = aVector(bbMax.x, bbMin.y, bbMax.z);
  v[4] = aVector(bbMax.x, bbMax.y, bbMin.z);
  v[5] = aVector(bbMax.x, bbMin.y, bbMin.z);
  v[6] = aVector(bbMin.x, bbMax.y, bbMin.z);
  v[7] = aVector(bbMin.x, bbMin.y, bbMin.z);

  { //I
    { //a
      bb[0] = v[0].x;
      bb[1] = v[0].y;
      bb[2] = v[0].z;

      bb[3] = v[1].x;
      bb[4] = v[1].y;
      bb[5] = v[1].z;

      bb[6] = v[2].x;
      bb[7] = v[2].y;
      bb[8] = v[2].z;
    }
    { //b
      bb[9] = v[0].x;
      bb[10] = v[0].y;
      bb[11] = v[0].z;

      bb[12] = v[2].x;
      bb[13] = v[2].y;
      bb[14] = v[2].z;

      bb[15] = v[3].x;
      bb[16] = v[3].y;
      bb[17] = v[3].z;
    }
  }
  { //II
    { //a
      bb[18] = v[7].x;
      bb[19] = v[7].y;
      bb[20] = v[7].z;

      bb[21] = v[6].x;
      bb[22] = v[6].y;
      bb[23] = v[6].z;

      bb[24] = v[1].x;
      bb[25] = v[1].y;
      bb[26] = v[1].z;
    }
    { //b
      bb[27] = v[7].x;
      bb[28] = v[7].y;
      bb[29] = v[7].z;

      bb[30] = v[1].x;
      bb[31] = v[1].y;
      bb[32] = v[1].z;

      bb[33] = v[0].x;
      bb[34] = v[0].y;
      bb[35] = v[0].z;
    }
  }
  { //III
    { //a
      bb[36] = v[1].x;
      bb[37] = v[1].y;
      bb[38] = v[1].z;

      bb[39] = v[6].x;
      bb[40] = v[6].y;
      bb[41] = v[6].z;

      bb[42] = v[4].x;
      bb[43] = v[4].y;
      bb[44] = v[4].z;
    }
    { //b
      bb[45] = v[1].x;
      bb[46] = v[1].y;
      bb[47] = v[1].z;

      bb[48] = v[4].x;
      bb[49] = v[4].y;
      bb[50] = v[4].z;

      bb[51] = v[2].x;
      bb[52] = v[2].y;
      bb[53] = v[2].z;
    }
  }
  { //IV
    { //a
      bb[54] = v[2].x;
      bb[55] = v[2].y;
      bb[56] = v[2].z;

      bb[57] = v[3].x;
      bb[58] = v[3].y;
      bb[59] = v[3].z;

      bb[60] = v[4].x;
      bb[61] = v[4].y;
      bb[62] = v[4].z;
    }
    { //b
      bb[63] = v[3].x;
      bb[64] = v[3].y;
      bb[65] = v[3].z;

      bb[66] = v[4].x;
      bb[67] = v[4].y;
      bb[68] = v[4].z;

      bb[69] = v[5].x;
      bb[70] = v[5].y;
      bb[71] = v[5].z;
    }
  }
  { //V
    { //a
      bb[72] = v[3].x;
      bb[73] = v[3].y;
      bb[74] = v[3].z;

      bb[75] = v[5].x;
      bb[76] = v[5].y;
      bb[77] = v[5].z;

      bb[78] = v[7].x;
      bb[79] = v[7].y;
      bb[80] = v[7].z;
    }
    { //b
      bb[81] = v[3].x;
      bb[82] = v[3].y;
      bb[83] = v[3].z;

      bb[84] = v[7].x;
      bb[85] = v[7].y;
      bb[86] = v[7].z;

      bb[87] = v[0].x;
      bb[88] = v[0].y;
      bb[89] = v[0].z;
    }
  }
  { //VI
    { //a
      bb[90] = v[5].x;
      bb[91] = v[5].y;
      bb[92] = v[5].z;

      bb[93] = v[4].x;
      bb[94] = v[4].y;
      bb[95] = v[4].z;

      bb[96] = v[6].x;
      bb[97] = v[6].y;
      bb[98] = v[6].z;
    }
    { //b
      bb[99] = v[5].x;
      bb[100] = v[5].y;
      bb[101] = v[5].z;

      bb[102] = v[6].x;
      bb[103] = v[6].y;
      bb[104] = v[6].z;

      bb[105] = v[7].x;
      bb[106] = v[7].y;
      bb[107] = v[7].z;
    }
  }
}

//=============================================================================

aSurface::~aSurface()
{
  asRemoveSurfaceData();
}

//=============================================================================
